﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;
using WooCoo.Data;
using WooCoo.DataPortal.Server.Hosts;
using WooCoo.DataPortal.Server.Hosts.WcfChannel;

namespace WooCoo.DataPortal.Client
{
    /// <summary>
    /// Wcf门户代理,具有约定的客户端与服务端的处理方式   
    /// </summary>
    /// <remarks>
    /// UI项目命名方式:项目名称+.Client,例EmailManager.Client
    /// 中间接口命名方式:项目名称+.Common,例EmailManager.Common
    /// 服务端命名方式:项目名称+.Service,例EmailManager.Service
    /// IIS服务宿主文件的命名方式:以中间接口项目的命名空间命名,例邮件服务文件为:EmailManager.Common.svc
    /// </remarks>
    public class WcfProxy : IDataPortalProxy
    {
        private static Dictionary<string, ChannelFactory> channelFactories = new Dictionary<string, ChannelFactory>();
        private static object syncHelper = new object();

        private const string _endPoint = "default";
        public const string DefaultHttpBinding = "defaultHttpBinding";
        /// <summary>
        /// 
        /// 通过配置节点名,从通道工厂缓存中取得对应的Channel
        /// </summary>
        /// <typeparam name="TChannel"></typeparam>
        /// <param name="moduleName">配置节点名</param>
        /// <returns></returns>
        private static ChannelFactory<TChannel> GetChannelFactory<TChannel>()
        {
            string portalUrl, key;
            Type tchType = typeof(TChannel);
            string strType = tchType.ToString();
            string endpointConfigurationName = tchType.FullName;
            portalUrl = ApplicationContext.DataPortalUrlString;
            
            key = portalUrl + strType;

            ChannelFactory<TChannel> channelFactory = null;
            if (channelFactories.ContainsKey(key))
            {
                channelFactory = channelFactories[key] as ChannelFactory<TChannel>;
            }
            if (channelFactory == null)
            {
                string serviceUrl = portalUrl + tchType.FullName + ".svc";
                EndpointAddress url = new EndpointAddress(serviceUrl);
                
                channelFactory = new ChannelFactory<TChannel>(endpointConfigurationName, url);
                
                lock (syncHelper)
                {
                    channelFactories[key] = channelFactory;
                }

            }
            return channelFactory;
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        /// <typeparam name="TChannel">WCF形式声明的接口类型</typeparam>
        /// <typeparam name="TReturn">返回类型</typeparam>
        /// <param name="func">接口方法委托</param>
        /// <param name="moduleName">配置模块名</param>
        /// <returns>返回TReturn类型</returns>
        public TReturn Invoke<TChannel, TReturn>(System.Func<TChannel, TReturn> func, string moduleName)
        {
            TReturn result = default(TReturn);
            ChannelFactory<TChannel> factory = GetChannelFactory<TChannel>();
            ICommunicationObject channel = null;
            try
            {
                TChannel proxy = factory.CreateChannel();
                channel = proxy as ICommunicationObject;
                result = func(proxy);
                channel.Close();
            }
            catch (CommunicationException ex)
            {
                if (null != channel)
                {
                    channel.Abort();
                }
                factory = null;
                throw ex;
            }
            catch (TimeoutException ex)
            {
                if (null != channel)
                    channel.Abort();
                throw ex;
            }
            catch (System.Exception ex)
            {
                if (null != channel)
                    channel.Close();
                throw ex;
            }
            return result;
        }

        #region IDataPortalProxy Members
        
        public bool IsServerRemote
        {
            get { return true; }
        }

        public object SerializeObject(object value)
        {
            return value;
        }

        public T DeserializeObject<T>(DataPortalResult result)
        {
            return (T)result.ReturnObject;
        }

        public DataPortalResult Create(Type objectType, object criteria, DataPortalContext context)
        {
            CriteriaRequest cr = new CriteriaRequest()
            {
                TypeName = objectType.AssemblyQualifiedName,
                //CriteriaData = criteria as Criteria,//MobileFormatter.Serialize(criteria),
                Context = context
            };
            WcfResponse response = this.Invoke<IWcfPortal, WcfResponse>(op => op.Create(cr), objectType.Module.Name);
            if (response.ErrorData != null)
            {
                Exception ex = ServiceExceptionDetail.GetException(response.ErrorData);
                throw new Exception(ex.Message, ex);
            }
            object result = response.ObjectData;
            if (result is Exception)
                throw (Exception)result;
            return (DataPortalResult)result;
        }

        public DataPortalResult Fetch(System.Type objectType, object criteria, DataPortalContext context)
        {
            CriteriaRequest cr = new CriteriaRequest()
            {
                TypeName = objectType.AssemblyQualifiedName,
                //CriteriaData = criteria as Criteria,//MobileFormatter.Serialize(criteria),
                Context = context
            };
            WcfResponse response = Invoke<IWcfPortal, WcfResponse>(op => op.Fetch(cr), objectType.Module.Name);
            //对Wcf的错误进行封装，使之返回原始错误
            if (response.ErrorData != null)
            {
                Exception ex = ServiceExceptionDetail.GetException(response.ErrorData);
                throw new Exception(ex.Message, ex);
            }
            object result = response.ObjectData;
            if (result is Exception)
                throw (Exception)result;
            return (DataPortalResult)result;
        }

        public DataPortalResult Update(object obj, DataPortalContext context)
        {
            UpdateRequest ur = new UpdateRequest()
            {
                ObjectData = obj,
                Context = context
            };
            WcfResponse response = Invoke<IWcfPortal, WcfResponse>(op => op.Update(ur), obj.GetType().Module.Name);
            if (response.ErrorData != null)
            {
                Exception ex = ServiceExceptionDetail.GetException(response.ErrorData);
                throw new Exception(ex.Message, ex);
            }
            object result = response.ObjectData;
            if (result is Exception)
                throw (Exception)result;
            return (DataPortalResult)result;
        }

        public DataPortalResult Delete(System.Type objectType, object criteria, DataPortalContext context)
        {
            CriteriaRequest cr = new CriteriaRequest()
            {
                TypeName = objectType.AssemblyQualifiedName,
                //CriteriaData = criteria as Criteria,//MobileFormatter.Serialize(criteria),
                Context = context
            };
            WcfResponse response = Invoke<IWcfPortal, WcfResponse>(op => op.Delete(cr), objectType.Module.Name);
            if (response.ErrorData != null)
            {
                Exception ex = ServiceExceptionDetail.GetException(response.ErrorData);
                throw new Exception(ex.Message, ex);
            }
            object result = response.ObjectData;
            if (result is Exception)
                throw (Exception)result;
            return (DataPortalResult)result;
        }

        public DataPortalResult Execute(ActionInfo requestMethod, DataPortalContext context)
        {
            ExcuteRequest er = new ExcuteRequest()
            {
                ActionInfo = requestMethod,
                Context = context
            };
            WcfResponse response = Invoke<IWcfPortal, WcfResponse>(op => op.Execute(er), requestMethod.ContractName);
            if (response.ErrorData != null)
            {
                Exception ex = ServiceExceptionDetail.GetException(response.ErrorData);
                throw new Exception(ex.Message, ex);
            }
            object result = response.ObjectData;
            if (result is Exception)
                throw (Exception)result;
            return (DataPortalResult)result;
        }
        #endregion



        public Uri BaseAddress
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public DataPortalResult Create(Type objectType,object criteria)
        {
            throw new NotImplementedException();
        }

        public DataPortalResult Fetch<T>(object criteria)
        {
            throw new NotImplementedException();
        }

        public DataPortalResult Insert(object obj)
        {
            throw new NotImplementedException();
        }

        public DataPortalResult Update(object obj)
        {
            throw new NotImplementedException();
        }

        public DataPortalResult Delete(object obj)
        {
            throw new NotImplementedException();
        }

        public DataPortalResult Execute(ActionInfo requestMethod)
        {
            throw new NotImplementedException();
        }

        public QueryComponents Translate(System.Linq.Expressions.Expression e)
        {
            throw new NotImplementedException();
        }
    }
}
